<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 2.0.26">
<link rel="icon" type="image/svg" href="../../../../_docs/_Assets/Logo.svg">
<title>Basic MVVM Sample</title>
<style>
@import url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css);

:root {
    --background: #fff;
    --black-color: #000;
    --background-yellow: #ff0;
    --border: #c0c0c0;
    --gray-color: #6c818f;
    --link-color-default: var(--accent-primary-color);
    --foreground-color: #222222;
    --accent-primary-color: #3578e5;
    --accent-secondary-color: #102445;
    --border2: #dddddd;
    --block-quote-color: #748590;
    --pre-background-color: #f2f2f2;
    --dark-gray-color: #333333;
    --kdb-background-color: #F7F7F7;
    --header-color: var(--accent-primary-color);
    --link-hover-color: #102445;
    --toc-border-color: #d9d9d9;
    --exampleblock-border-color: #e6e6e6;
    --em-color: #373737;
    --admotion-background-color: #e6e6e6;
    --sidebar-background-color: #e8ecef;
    --border-radius-default: 6px;
}

/* normalize.css v2.1.1 | MIT License | git.io/normalize */
/* ========================================================================== HTML5 display definitions ========================================================================== */
/** Correct `block` display not defined in IE 8/9. */
article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary {
    display: block;
}

/** Correct `inline-block` display not defined in IE 8/9. */
audio, canvas, video {
    display: inline-block;
}

/** Prevent modern browsers from displaying `audio` without controls. Remove excess height in iOS 5 devices. */
audio:not([controls]) {
    display: none;
    height: 0;
}

/** Address styling not present in IE 8/9. */
[hidden] {
    display: none;
}

/* ========================================================================== Base ========================================================================== */
/** 1. Prevent system color scheme's background color being used in Firefox, IE, and Opera. 2. Prevent system color scheme's text color being used in Firefox, IE, and Opera. 3. Set default font family to sans-serif. 4. Prevent iOS text size adjust after orientation change, without disabling user zoom. */
html {
    background: var(--background); /* 1 */
    color: var(--black-color); /* 2 */
    font-family: sans-serif; /* 3 */
    -ms-text-size-adjust: 100%; /* 4 */
    -webkit-text-size-adjust: 100%; /* 4 */
}

/** Remove default margin. */
body {
    margin: 0;
}

/* ========================================================================== Links ========================================================================== */
/** Address `outline` inconsistency between Chrome and other browsers. */
a:focus {
    outline: thin dotted;
}

/** Improve readability when focused and also mouse hovered in all browsers. */
a:active, a:hover {
    outline: 0;
}

/* ========================================================================== Typography ========================================================================== */
/** Address variable `h1` font-size and margin within `section` and `article` contexts in Firefox 4+, Safari 5, and Chrome. */
h1 {
    font-size: 2em;
    margin: 0.67em 0;
}

/** Address styling not present in IE 8/9, Safari 5, and Chrome. */
abbr[title] {
    border-bottom: 1px dotted;
}

/** Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. */
b, strong {
    font-weight: bold;
}

/** Address styling not present in Safari 5 and Chrome. */
dfn {
    font-style: italic;
}

/** Address differences between Firefox and other browsers. */
hr {
    -moz-box-sizing: content-box;
    box-sizing: content-box;
    height: 0;
}

/** Address styling not present in IE 8/9. */
mark {
    background: var(--background-yellow);
    color: var(--black-color);
}

/** Correct font family set oddly in Safari 5 and Chrome. */
code, kbd, pre, samp {
    font-family: monospace, serif;
    font-size: 1em;
}

/** Improve readability of pre-formatted text in all browsers. */
pre {
    white-space: pre-wrap;
}

/** Set consistent quote types. */
q {
    quotes: "\201C" "\201D" "\2018" "\2019";
}

/** Address inconsistent and variable font size in all browsers. */
small {
    font-size: 80%;
}

/** Prevent `sub` and `sup` affecting `line-height` in all browsers. */
sub, sup {
    font-size: 75%;
    line-height: 0;
    position: relative;
    vertical-align: baseline;
}

sup {
    top: -0.5em;
}

sub {
    bottom: -0.25em;
}

/* ========================================================================== Embedded content ========================================================================== */
/** Remove border when inside `a` element in IE 8/9. */
img {
    border: 0;
}

/** Correct overflow displayed oddly in IE 9. */
svg:not(:root) {
    overflow: hidden;
}

/* ========================================================================== Figures ========================================================================== */
/** Address margin not present in IE 8/9 and Safari 5. */
figure {
    margin: 0;
}

/* ========================================================================== Forms ========================================================================== */
/** Define consistent border, margin, and padding. */
fieldset {
    border: 1px solid var(--border);
    margin: 0 2px;
    padding: 0.35em 0.625em 0.75em;
}

/** 1. Correct `color` not being inherited in IE 8/9. 2. Remove padding so people aren't caught out if they zero out fieldsets. */
legend {
    border: 0; /* 1 */
    padding: 0; /* 2 */
}

/** 1. Correct font family not being inherited in all browsers. 2. Correct font size not being inherited in all browsers. 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. */
button, input, select, textarea {
    font-family: inherit; /* 1 */
    font-size: 100%; /* 2 */
    margin: 0; /* 3 */
}

/** Address Firefox 4+ setting `line-height` on `input` using `!important` in the UA stylesheet. */
button, input {
    line-height: normal;
}

/** Address inconsistent `text-transform` inheritance for `button` and `select`. All other form control elements do not inherit `text-transform` values. Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. Correct `select` style inheritance in Firefox 4+ and Opera. */
button, select {
    text-transform: none;
}

/** 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. 2. Correct inability to style clickable `input` types in iOS. 3. Improve usability and consistency of cursor style between image-type `input` and others. */
button, html input[type="button"], input[type="reset"], input[type="submit"] {
    -webkit-appearance: button; /* 2 */
    cursor: pointer; /* 3 */
}

/** Re-set default cursor for disabled elements. */
button[disabled], html input[disabled] {
    cursor: default;
}

/** 1. Address box sizing set to `content-box` in IE 8/9. 2. Remove excess padding in IE 8/9. */
input[type="checkbox"], input[type="radio"] {
    box-sizing: border-box; /* 1 */
    padding: 0; /* 2 */
}

/** 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome (include `-moz` to future-proof). */
input[type="search"] {
    -webkit-appearance: textfield; /* 1 */
    -moz-box-sizing: content-box;
    -webkit-box-sizing: content-box; /* 2 */
    box-sizing: content-box;
}

/** Remove inner padding and search cancel button in Safari 5 and Chrome on OS X. */
input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration {
    -webkit-appearance: none;
}

/** Remove inner padding and border in Firefox 4+. */
button::-moz-focus-inner, input::-moz-focus-inner {
    border: 0;
    padding: 0;
}

/** 1. Remove default vertical scrollbar in IE 8/9. 2. Improve readability and alignment in all browsers. */
textarea {
    overflow: auto; /* 1 */
    vertical-align: top; /* 2 */
}

/* ========================================================================== Tables ========================================================================== */
/** Remove most spacing between table cells. */
table {
    border-collapse: collapse;
    border-spacing: 0;
}

*, *:before, *:after {
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
}

html, body {
    font-size: 100%;
}

body {
    background: white;
    color: var(--foreground-color);
    padding: 0;
    margin: 0;
    font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif;
    font-weight: normal;
    font-style: normal;
    line-height: 1;
    position: relative;
    cursor: auto;
}

a:hover {
    cursor: pointer;
}

a:focus {
    outline: none;
}

img, object, embed {
    max-width: 100%;
    height: auto;
}

object, embed {
    height: 100%;
}

img {
    -ms-interpolation-mode: bicubic;
}

#map_canvas img, #map_canvas embed, #map_canvas object, .map_canvas img, .map_canvas embed, .map_canvas object {
    max-width: none !important;
}

.left {
    float: left !important;
}

.right {
    float: right !important;
}

.text-left {
    text-align: left !important;
}

.text-right {
    text-align: right !important;
}

.text-center {
    text-align: center !important;
}

.text-justify {
    text-align: justify !important;
}

.hide {
    display: none;
}

.antialiased, body {
    -webkit-font-smoothing: antialiased;
}

img {
    display: inline-block;
    vertical-align: middle;
}

textarea {
    height: auto;
    min-height: 50px;
}

select {
    width: 100%;
}

p.lead, .paragraph.lead > p, #preamble > .sectionbody > .paragraph:first-of-type p {
    font-size: 1.21875em;
    line-height: 1.6;
}

.subheader, #content #toctitle, .admonitionblock td.content > .title, .exampleblock > .title, .imageblock > .title, .videoblock > .title, .listingblock > .title, .literalblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, .sidebarblock > .title, .tableblock > .title, .verseblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title, .tableblock > caption {
    line-height: 1.4;
    color: var(--accent-primary-color);
    font-weight: 300;
    margin-top: 0.5em;
    margin-bottom: 0.5em;
}

/* Typography resets */
div, dl, dt, dd, ul, ol, li, h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6, pre, form, p, blockquote, th, td {
    margin: 0;
    /*padding: 0;*/
    direction: ltr;
}

/* Default Link Styles */
a {
    color: var(--link-color-default);
    text-decoration: none;
    line-height: inherit;
}

a:hover, a:focus {
    color: var(--header-color);
}

a img {
    border: none;
}

/* Default paragraph styles */
p {
    font-family: inherit;
    font-weight: normal;
    font-size: 1em;
    line-height: 1.5;
    margin-bottom: 1.25em;
    text-rendering: optimizeLegibility;
}

p aside {
    font-size: 0.875em;
    line-height: 1.35;
    font-style: italic;
}

/* Default header styles */
h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 {
    font-family: ff-meta-web-pro-1, ff-meta-web-pro-2, Arial, "Helvetica Neue", sans-serif;
    font-weight: bold;
    font-style: normal;
    color: var(--accent-primary-color);
    text-rendering: optimizeLegibility;
    margin-top: 1em;
    margin-bottom: 0.5em;
    line-height: 1.2125em;
}

h1 small, h2 small, h3 small, #toctitle small, .sidebarblock > .content > .title small, h4 small, h5 small, h6 small {
    font-size: 60%;
    color: var(--accent-secondary-color);
    line-height: 0;
}

h1 {
    font-size: 2.125em;
}

h2 {
    font-size: 1.6875em;
}

h3, #toctitle, .sidebarblock > .content > .title {
    font-size: 1.375em;
}

h4 {
    font-size: 1.125em;
}

h5 {
    font-size: 1.125em;
}

h6 {
    font-size: 1em;
}

hr {
    border: solid var(--border2);
    border-width: 1px 0 0;
    clear: both;
    margin: 1.25em 0 1.1875em;
    height: 0;
}

/* Helpful Typography Defaults */
em, i {
    font-style: italic;
    line-height: inherit;
}

strong, b {
    font-weight: bold;
    line-height: inherit;
}

small {
    font-size: 60%;
    line-height: inherit;
}

code {
    font-family: "Consolas", "Deja Vu Sans Mono", "Bitstream Vera Sans Mono", monospace;
    font-weight: normal;
    color: var(--link-color-default);
}

/* Lists */
ul, ol, dl {
    font-size: 1em;
    line-height: 1.5;
    margin-bottom: 1.25em;
    list-style-position: outside;
    font-family: inherit;
}

ul, ol {
    margin-left: 0;
}

/* Unordered Lists */
ul li ul, ul li ol {
    margin-left: 1.25em;
    margin-bottom: 0;
    font-size: 1em; /* Override nested font-size change */
}

ul.square li ul, ul.circle li ul, ul.disc li ul {
    list-style: inherit;
}

ul.square {
    list-style-type: square;
}

ul.circle {
    list-style-type: circle;
}

ul.disc {
    list-style-type: disc;
}

ul.no-bullet {
    list-style: none;
}

/* Ordered Lists */
ol li ul, ol li ol {
    margin-left: 1.25em;
    margin-bottom: 0;
}

/* Definition Lists */
dl dt {
    margin-bottom: 0.3em;
    font-weight: bold;
}

dl dd {
    margin-bottom: 0.75em;
}

/* Abbreviations */
abbr, acronym {
    text-transform: uppercase;
    font-size: 90%;
    color: black;
    border-bottom: 1px dotted var(--border2);
    cursor: help;
}

abbr {
    text-transform: none;
}

/* Blockquotes */
blockquote {
    margin: 0 0 1.25em;
    padding: 0.5625em 1.25em 0 1.1875em;
    border-left: 1px solid var(--border2);
}

blockquote cite {
    display: block;
    font-size: 0.8125em;
    color: var(--block-quote-color);
}

blockquote cite:before {
    content: "\2014 \0020";
}

blockquote cite a, blockquote cite a:visited {
    color: var(--block-quote-color);
}

blockquote, blockquote p {
    line-height: 1.5;
    color: var(--accent-secondary-color);
}

/* Microformats */
.vcard {
    display: inline-block;
    margin: 0 0 1.25em 0;
    border: 1px solid var(--border2);
    padding: 0.625em 0.75em;
}

.vcard li {
    margin: 0;
    display: block;
}

.vcard .fn {
    font-weight: bold;
    font-size: 0.9375em;
}

.vevent .summary {
    font-weight: bold;
}

.vevent abbr {
    cursor: auto;
    text-decoration: none;
    font-weight: bold;
    border: none;
    padding: 0 0.0625em;
}

@media only screen and (min-width: 768px) {
    h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 {
        line-height: 1.4;
    }

    h1 {
        font-size: 2.75em;
    }

    h2 {
        font-size: 2.3125em;
    }

    h3, #toctitle, .sidebarblock > .content > .title {
        font-size: 1.6875em;
    }

    h4 {
        font-size: 1.4375em;
    }
}

/* Print styles.  Inlined to avoid required HTTP connection: www.phpied.com/delay-loading-your-print-css/ Credit to Paul Irish and HTML5 Boilerplate (html5boilerplate.com)
*/
.print-only {
    display: none !important;
}

@media print {
    * {
        background: transparent !important;
        color: var(--black-color) !important; /* Black prints faster: h5bp.com/s */
        box-shadow: none !important;
        text-shadow: none !important;
    }

    a, a:visited {
        text-decoration: underline;
    }

    a[href]:after {
        content: " (" attr(href) ")";
    }

    abbr[title]:after {
        content: " (" attr(title) ")";
    }

    .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after {
        content: "";
    }

    pre, blockquote {
        border: 1px solid #999;
        page-break-inside: avoid;
    }

    thead {
        display: table-header-group; /* h5bp.com/t */
    }

    tr, img {
        page-break-inside: avoid;
    }

    img {
        max-width: 100% !important;
    }

    @page {
        margin: 0.5cm;
    }

    p, h2, h3, #toctitle, .sidebarblock > .content > .title {
        orphans: 3;
        widows: 3;
    }

    h2, h3, #toctitle, .sidebarblock > .content > .title {
        page-break-after: avoid;
    }

    .hide-on-print {
        display: none !important;
    }

    .print-only {
        display: block !important;
    }

    .hide-for-print {
        display: none !important;
    }

    .show-for-print {
        display: inherit !important;
    }
}

/* Tables */
table {
    background: white;
    margin-bottom: 1.25em;
    border: solid 0 var(--border2);
}

table thead, table tfoot {
    background: none;
    font-weight: bold;
}

table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td {
    padding: 1px 8px 1px 5px;
    font-size: 1em;
    color: var(--foreground-color);
    text-align: left;
}

table tr th, table tr td {
    padding: 1px 8px 1px 5px;
    font-size: 1em;
    color: var(--foreground-color);
}

table tr.even, table tr.alt, table tr:nth-of-type(even) {
    background: none;
}

table thead tr th, table tfoot tr th, table tbody tr td, table tr td, table tfoot tr td {
    display: table-cell;
    line-height: 1.5;
}

.clearfix:before, .clearfix:after, .float-group:before, .float-group:after {
    content: " ";
    display: table;
}

.clearfix:after, .float-group:after {
    clear: both;
}

*:not(pre) > code {
    font-size: 0.95em;
    padding: 0;
    white-space: nowrap;
    background-color: var(--pre-background-color);
    border: 0 solid var(--border2);
    -webkit-border-radius: var(--border-radius-default);
    border-radius: var(--border-radius-default);
    text-shadow: none;
}

pre, pre > code {
    line-height: 1.2;
    color: inherit;
    font-family: "Consolas", "Deja Vu Sans Mono", "Bitstream Vera Sans Mono", monospace;
    font-weight: normal;
}

kbd.keyseq {
    color: var(--dark-gray-color);
}

kbd:not(.keyseq) {
    display: inline-block;
    color: black;
    font-size: 0.75em;
    line-height: 1.4;
    background-color: var(--kdb-background-color);
    border: 1px solid #ccc;
    -webkit-border-radius: 3px;
    border-radius: 3px;
    -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 2px white inset;
    box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 2px white inset;
    margin: -0.15em 0.15em 0 0.15em;
    padding: 0.2em 0.6em 0.2em 0.5em;
    vertical-align: middle;
    white-space: nowrap;
}

kbd kbd:first-child {
    margin-left: 0;
}

kbd kbd:last-child {
    margin-right: 0;
}

.menuseq, .menu {
    color: black;
}

p a > code:hover {
    color: var(--link-hover-color);
}

#header, #content, #footnotes, #footer {
    width: 100%;
    margin-left: auto;
    margin-right: auto;
    margin-top: 0;
    margin-bottom: 0;
    max-width: 62.5em;
    *zoom: 1;
    position: relative;
    padding-left: 0.9375em;
    padding-right: 0.9375em;
}

#header:before, #header:after, #content:before, #content:after, #footnotes:before, #footnotes:after, #footer:before, #footer:after {
    content: " ";
    display: table;
}

#header:after, #content:after, #footnotes:after, #footer:after {
    clear: both;
}

#header {
    margin-bottom: 2.5em;
}

#header > h1 {
    color: var(--header-color);
    font-weight: bold;
    border-bottom: 1px solid var(--border2);
    margin-bottom: -28px;
    padding-bottom: 32px;
}

#header span {
    color: var(--accent-secondary-color);
}

#header #revnumber {
    text-transform: capitalize;
}

#header br {
    display: none;
}

#header br + span {
    padding-left: 3px;
}

#header br + span:before {
    content: "\2013 \0020";
}

#header br + span.author {
    padding-left: 0;
}

#header br + span.author:before {
    content: ", ";
}

#toc {
    border-bottom: 1px solid var(--border2);
    padding-bottom: 1.25em;
}

#toc > ul {
    margin-left: 0.25em;
}

#toc ul.sectlevel0 > li > a {
    font-style: italic;
}

#toc ul.sectlevel0 ul.sectlevel1 {
    margin-left: 0;
    margin-top: 0.5em;
    margin-bottom: 0.5em;
}

#toc ul {
    list-style-type: none;
}

#toctitle {
    color: var(--accent-primary-color);
}

@media only screen and (min-width: 1280px) {
    body.toc2 {
        padding-left: 20em;
    }

    #toc.toc2 {
        position: fixed;
        width: 20em;
        left: 0;
        top: 0;
        border-right: 1px solid var(--border2);
        border-bottom: 0;
        z-index: 1000;
        padding: 44px 1em 1em;
        height: 100%;
        overflow: auto;
    }

    #toc.toc2 > ul {
        padding-left: 1em;
    }

    #toc.toc2 #toctitle {
        margin-top: 0;
    }

    #toc.toc2 > ul {
        font-size: .95em;
    }

    #toc.toc2 ul ul {
        margin-left: 0;
        padding-left: 1.25em;
    }

    #toc.toc2 ul.sectlevel0 ul.sectlevel1 {
        padding-left: 0;
        margin-top: 0.5em;
        margin-bottom: 0.5em;
    }

    body.toc2.toc-right {
        padding-left: 0;
        padding-right: 20em;
    }

    body.toc2.toc-right #toc.toc2 {
        border-right: 0;
        border-left: 1px solid var(--border2);
        left: auto;
        right: 0;
    }
}

#content #toc {
    border-style: solid;
    border-width: 1px;
    border-color: var(--toc-border-color);
    margin-bottom: 1.25em;
    padding: 1.25em;
    background: var(--pre-background-color);
    border-width: 0;
    -webkit-border-radius: var(--border-radius-default);
    border-radius: var(--border-radius-default);
}

#content #toc > :first-child {
    margin-top: 0;
}

#content #toc > :last-child {
    margin-bottom: 0;
}

#content #toc a {
    color: var(--gray-color);
    text-decoration: none;
}

#content #toctitle {
    font-weight: bold;
    font-family: ff-meta-web-pro-1, ff-meta-web-pro-2, Arial, "Helvetica Neue", sans-serif;
    font-size: 1em;
    padding-left: 0.125em;
}

#footer {
    max-width: 100%;
    background-color: black;
    padding: 1.25em;
}

#footer-text {
    color: white;
    line-height: 1.35;
}

.sect1 {
    padding-bottom: 1.25em;
}

.sect1 + .sect1 {
    border-top: 1px solid var(--border2);
}

#content h1 > a.anchor, h2 > a.anchor, h3 > a.anchor, #toctitle > a.anchor, .sidebarblock > .content > .title > a.anchor, h4 > a.anchor, h5 > a.anchor, h6 > a.anchor {
    position: absolute;
    width: 1em;
    margin-left: -1em;
    display: block;
    text-decoration: none;
    visibility: hidden;
    text-align: center;
    font-weight: normal;
}

#content h1 > a.anchor:before, h2 > a.anchor:before, h3 > a.anchor:before, #toctitle > a.anchor:before, .sidebarblock > .content > .title > a.anchor:before, h4 > a.anchor:before, h5 > a.anchor:before, h6 > a.anchor:before {
    content: '\00A7';
    font-size: .85em;
    vertical-align: text-top;
    display: block;
    margin-top: 0.05em;
}

#content h1:hover > a.anchor, #content h1 > a.anchor:hover, h2:hover > a.anchor, h2 > a.anchor:hover, h3:hover > a.anchor, #toctitle:hover > a.anchor, .sidebarblock > .content > .title:hover > a.anchor, h3 > a.anchor:hover, #toctitle > a.anchor:hover, .sidebarblock > .content > .title > a.anchor:hover, h4:hover > a.anchor, h4 > a.anchor:hover, h5:hover > a.anchor, h5 > a.anchor:hover, h6:hover > a.anchor, h6 > a.anchor:hover {
    visibility: visible;
}

#content h1 > a.link, h2 > a.link, h3 > a.link, #toctitle > a.link, .sidebarblock > .content > .title > a.link, h4 > a.link, h5 > a.link, h6 > a.link {
    color: var(--accent-primary-color);
    text-decoration: none;
}

#content h1 > a.link:hover, h2 > a.link:hover, h3 > a.link:hover, #toctitle > a.link:hover, .sidebarblock > .content > .title > a.link:hover, h4 > a.link:hover, h5 > a.link:hover, h6 > a.link:hover {
    color: var(--link-hover-color);
}

.imageblock, .literalblock, .listingblock, .verseblock, .videoblock {
    margin-bottom: 1.25em;
}

.admonitionblock td.content > .title, .exampleblock > .title, .imageblock > .title, .videoblock > .title, .listingblock > .title, .literalblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, .sidebarblock > .title, .tableblock > .title, .verseblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title {
    text-align: left;
    font-weight: bold;
}

.tableblock > caption {
    text-align: left;
    font-weight: bold;
    white-space: nowrap;
    overflow: visible;
    max-width: 0;
}

table.tableblock #preamble > .sectionbody > .paragraph:first-of-type p {
    font-size: inherit;
}

.admonitionblock > table {
    border: 0;
    background: none;
    width: 100%;
}

.admonitionblock > table td.icon {
    text-align: center;
    width: 60px;
    padding: 0;
    font-size: 1.5em;
}

.admonitionblock > table td.icon img {
    max-width: none;
}

.admonitionblock > table td.icon .title {
    font-weight: bold;
    text-transform: uppercase;
}

.admonitionblock > table td.content {
    /*padding-left: 1.125em;*/
    /*padding-right: 1.25em;*/
    border-left: 1px solid var(--border2);
    color: var(--accent-secondary-color);
}

.admonitionblock > table td.content > :last-child > :last-child {
    margin-bottom: 0;
}

.exampleblock > .content {
    border-style: solid;
    border-width: 1px;
    border-color: var(--exampleblock-border-color);
    margin-bottom: 1.25em;
    padding: 1.25em;
    background: white;
    -webkit-border-radius: var(--border-radius-default);
    border-radius: var(--border-radius-default);
}

.exampleblock > .content > :first-child {
    margin-top: 0;
}

.exampleblock > .content > :last-child {
    margin-bottom: 0;
}

.exampleblock > .content h1, .exampleblock > .content h2, .exampleblock > .content h3, .exampleblock > .content #toctitle, .sidebarblock.exampleblock > .content > .title, .exampleblock > .content h4, .exampleblock > .content h5, .exampleblock > .content h6, .exampleblock > .content p {
    color: var(--dark-gray-color);
}

.exampleblock > .content h1, .exampleblock > .content h2, .exampleblock > .content h3, .exampleblock > .content #toctitle, .sidebarblock.exampleblock > .content > .title, .exampleblock > .content h4, .exampleblock > .content h5, .exampleblock > .content h6 {
    line-height: 1;
    margin-bottom: 0.625em;
}

.exampleblock > .content h1.subheader, .exampleblock > .content h2.subheader, .exampleblock > .content h3.subheader, .exampleblock > .content .subheader#toctitle, .sidebarblock.exampleblock > .content > .subheader.title, .exampleblock > .content h4.subheader, .exampleblock > .content h5.subheader, .exampleblock > .content h6.subheader {
    line-height: 1.4;
}

.exampleblock.result > .content {
    -webkit-box-shadow: 0 1px 8px var(--toc-border-color);
    box-shadow: 0 1px 8px var(--toc-border-color);
}

.sidebarblock {
    border-style: solid;
    border-width: 1px;
    border-color: var(--toc-border-color);
    margin-bottom: 1.25em;
    padding: 1.25em;
    background: var(--pre-background-color);
    -webkit-border-radius: var(--border-radius-default);
    border-radius: var(--border-radius-default);
}

.sidebarblock > :first-child {
    margin-top: 0;
}

.sidebarblock > :last-child {
    margin-bottom: 0;
}

.sidebarblock h1, .sidebarblock h2, .sidebarblock h3, .sidebarblock #toctitle, .sidebarblock > .content > .title, .sidebarblock h4, .sidebarblock h5, .sidebarblock h6, .sidebarblock p {
    color: var(--dark-gray-color);
}

.sidebarblock h1, .sidebarblock h2, .sidebarblock h3, .sidebarblock #toctitle, .sidebarblock > .content > .title, .sidebarblock h4, .sidebarblock h5, .sidebarblock h6 {
    line-height: 1;
    margin-bottom: 0.625em;
}

.sidebarblock h1.subheader, .sidebarblock h2.subheader, .sidebarblock h3.subheader, .sidebarblock .subheader#toctitle, .sidebarblock > .content > .subheader.title, .sidebarblock h4.subheader, .sidebarblock h5.subheader, .sidebarblock h6.subheader {
    line-height: 1.4;
}

.sidebarblock > .content > .title {
    color: var(--gray-color);
    margin-top: 0;
    line-height: 1.5;
}

.exampleblock > .content > :last-child > :last-child, .exampleblock > .content .olist > ol > li:last-child > :last-child, .exampleblock > .content .ulist > ul > li:last-child > :last-child, .exampleblock > .content .qlist > ol > li:last-child > :last-child, .sidebarblock > .content > :last-child > :last-child, .sidebarblock > .content .olist > ol > li:last-child > :last-child, .sidebarblock > .content .ulist > ul > li:last-child > :last-child, .sidebarblock > .content .qlist > ol > li:last-child > :last-child {
    margin-bottom: 0;
}

.literalblock > .content pre, .listingblock > .content pre {
    background: #eeeeee;
    border-width: 1px;
    border-style: solid;
    border-color: #cccccc;
    -webkit-border-radius: var(--border-radius-default);
    border-radius: var(--border-radius-default);
    padding: 0.5em;
    word-wrap: break-word;
}

.literalblock > .content pre.nowrap, .listingblock > .content pre.nowrap {
    overflow-x: auto;
    white-space: pre;
    word-wrap: normal;
}

.literalblock > .content pre > code, .listingblock > .content pre > code {
    display: block;
}

@media only screen {
    .literalblock > .content pre, .listingblock > .content pre {
        font-size: 0.76em;
    }
}

@media only screen and (min-width: 768px) {
    .literalblock > .content pre, .listingblock > .content pre {
        font-size: 0.855em;
    }
}

@media only screen and (min-width: 1280px) {
    .literalblock > .content pre, .listingblock > .content pre {
        font-size: 0.95em;
    }
}

.listingblock > .content {
    position: relative;
}

.listingblock:hover code[class*=" language-"]:before {
    text-transform: uppercase;
    font-size: 0.9em;
    color: #999;
    position: absolute;
    top: 0.375em;
    right: 0.375em;
}

.listingblock:hover code.asciidoc:before {
    content: "asciidoc";
}

.listingblock:hover code.clojure:before {
    content: "clojure";
}

.listingblock:hover code.css:before {
    content: "css";
}

.listingblock:hover code.groovy:before {
    content: "groovy";
}

.listingblock:hover code.html:before {
    content: "html";
}

.listingblock:hover code.java:before {
    content: "java";
}

.listingblock:hover code.javascript:before {
    content: "javascript";
}

.listingblock:hover code.python:before {
    content: "python";
}

.listingblock:hover code.ruby:before {
    content: "ruby";
}

.listingblock:hover code.scss:before {
    content: "scss";
}

.listingblock:hover code.xml:before {
    content: "xml";
}

.listingblock:hover code.yaml:before {
    content: "yaml";
}

.listingblock.terminal pre .command:before {
    content: attr(data-prompt);
    padding-right: 0.5em;
    color: #999;
}

.listingblock.terminal pre .command:not([data-prompt]):before {
    content: '$';
}

table.pyhltable {
    border: 0;
    margin-bottom: 0;
}

table.pyhltable td {
    vertical-align: top;
    padding-top: 0;
    padding-bottom: 0;
}

table.pyhltable td.code {
    padding-left: .75em;
    padding-right: 0;
}

.highlight.pygments .lineno, table.pyhltable td:not(.code) {
    color: #999;
    padding-left: 0;
    padding-right: .5em;
    border-right: 1px solid var(--border2);
}

.highlight.pygments .lineno {
    display: inline-block;
    margin-right: .25em;
}

table.pyhltable .linenodiv {
    background-color: transparent !important;
    padding-right: 0 !important;
}

.quoteblock {
    margin: 0 0 1.25em;
    padding: 0.5625em 1.25em 0 1.1875em;
    border-left: 1px solid var(--border2);
}

.quoteblock blockquote {
    margin: 0 0 1.25em 0;
    padding: 0 0 0.5625em 0;
    border: 0;
}

.quoteblock blockquote > .paragraph:last-child p {
    margin-bottom: 0;
}

.quoteblock .attribution {
    margin-top: -.25em;
    padding-bottom: 0.5625em;
    font-size: 0.8125em;
    color: var(--block-quote-color);
}

.quoteblock .attribution br {
    display: none;
}

.quoteblock .attribution cite {
    display: block;
    margin-bottom: 0.625em;
}

table thead th, table tfoot th {
    font-weight: bold;
}

table.tableblock.grid-all {
    border-collapse: separate;
    border-spacing: 1px;
    -webkit-border-radius: var(--border-radius-default);
    border-radius: var(--border-radius-default);
    border-top: 0 solid var(--border2);
    border-bottom: 0 solid var(--border2);
}

table.tableblock.frame-topbot, table.tableblock.frame-none {
    border-left: 0;
    border-right: 0;
}

table.tableblock.frame-sides, table.tableblock.frame-none {
    border-top: 0;
    border-bottom: 0;
}

table.tableblock td .paragraph:last-child p, table.tableblock td > p:last-child {
    margin-bottom: 0;
}

th.tableblock.halign-left, td.tableblock.halign-left {
    text-align: left;
}

th.tableblock.halign-right, td.tableblock.halign-right {
    text-align: right;
}

th.tableblock.halign-center, td.tableblock.halign-center {
    text-align: center;
}

th.tableblock.valign-top, td.tableblock.valign-top {
    vertical-align: top;
}

th.tableblock.valign-bottom, td.tableblock.valign-bottom {
    vertical-align: bottom;
}

th.tableblock.valign-middle, td.tableblock.valign-middle {
    vertical-align: middle;
}

p.tableblock.header {
    color: var(--foreground-color);
    font-weight: bold;
}

td > div.verse {
    white-space: pre;
}

ol {
    margin-left: 0.25em;
}

ul li ol {
    margin-left: 0;
}

dl dd {
    margin-left: 1.125em;
}

dl dd:last-child, dl dd:last-child > :last-child {
    margin-bottom: 0;
}

ol > li p, ul > li p, ul dd, ol dd, .olist .olist, .ulist .ulist, .ulist .olist, .olist .ulist {
    margin-bottom: 0.625em;
}

ul.unstyled, ol.unnumbered, ul.checklist, ul.none {
    list-style-type: none;
}

ul.unstyled, ol.unnumbered, ul.checklist {
    margin-left: 0.625em;
}

ul.checklist li > p:first-child > i[class^="icon-check"]:first-child, ul.checklist li > p:first-child > input[type="checkbox"]:first-child {
    margin-right: 0.25em;
}

ul.checklist li > p:first-child > input[type="checkbox"]:first-child {
    position: relative;
    top: 1px;
}

ul.inline {
    margin: 0 auto 0.625em auto;
    margin-left: -1.375em;
    margin-right: 0;
    padding: 0;
    list-style: none;
    overflow: hidden;
}

ul.inline > li {
    list-style: none;
    float: left;
    margin-left: 1.375em;
    display: block;
}

ul.inline > li > * {
    display: block;
}

.unstyled dl dt {
    font-weight: normal;
    font-style: normal;
}

ol.arabic {
    list-style-type: decimal;
}

ol.decimal {
    list-style-type: decimal-leading-zero;
}

ol.loweralpha {
    list-style-type: lower-alpha;
}

ol.upperalpha {
    list-style-type: upper-alpha;
}

ol.lowerroman {
    list-style-type: lower-roman;
}

ol.upperroman {
    list-style-type: upper-roman;
}

ol.lowergreek {
    list-style-type: lower-greek;
}

.hdlist > table, .colist > table {
    border: 0;
    background: none;
}

.hdlist > table > tbody > tr, .colist > table > tbody > tr {
    background: none;
}

td.hdlist1 {
    padding-right: .8em;
    font-weight: bold;
}

td.hdlist1, td.hdlist2 {
    vertical-align: top;
}

.literalblock + .colist, .listingblock + .colist {
    margin-top: -0.5em;
}

.colist > table tr > td:first-of-type {
    padding: 0 .8em;
    line-height: 1;
}

.colist > table tr > td:last-of-type {
    padding: 0.25em 0;
}

.qanda > ol > li > p > em:only-child {
    color: var(--em-color);
}

.thumb, .th {
    line-height: 0;
    display: inline-block;
    border: solid 4px white;
    -webkit-box-shadow: 0 0 0 1px var(--border2);
    box-shadow: 0 0 0 1px var(--border2);
}

.imageblock.left, .imageblock[style*="float: left"] {
    margin: 0.25em 0.625em 1.25em 0;
}

.imageblock.right, .imageblock[style*="float: right"] {
    margin: 0.25em 0 1.25em 0.625em;
}

.imageblock > .title {
    margin-bottom: 0;
}

.imageblock.thumb, .imageblock.th {
    border-width: var(--border-radius-default);
}

.imageblock.thumb > .title, .imageblock.th > .title {
    padding: 0 0.125em;
}

.image.left, .image.right {
    margin-top: 0.25em;
    margin-bottom: 0.25em;
    display: inline-block;
    line-height: 0;
}

.image.left {
    margin-right: 0.625em;
}

.image.right {
    margin-left: 0.625em;
}

a.image {
    text-decoration: none;
}

span.footnote, span.footnoteref {
    vertical-align: super;
    font-size: 0.875em;
}

span.footnote a, span.footnoteref a {
    text-decoration: none;
}

#footnotes {
    padding-top: 0.75em;
    padding-bottom: 0.75em;
    margin-bottom: 0.625em;
}

#footnotes hr {
    width: 20%;
    min-width: 6.25em;
    margin: -.25em 0 .75em 0;
    border-width: 1px 0 0 0;
}

#footnotes .footnote {
    padding: 0 0.375em;
    line-height: 1.3;
    font-size: 0.875em;
    margin-left: 1.2em;
    text-indent: -1.2em;
    margin-bottom: .2em;
}

#footnotes .footnote a:first-of-type {
    font-weight: bold;
    text-decoration: none;
}

#footnotes .footnote:last-of-type {
    margin-bottom: 0;
}

#content #footnotes {
    margin-top: -0.625em;
    margin-bottom: 0;
    padding: 0.75em 0;
}

.gist .file-data > table {
    border: none;
    background: var(--background);
    width: 100%;
    margin-bottom: 0;
}

.gist .file-data > table td.line-data {
    width: 99%;
}

div.unbreakable {
    page-break-inside: avoid;
}

.big {
    font-size: larger;
}

.small {
    font-size: smaller;
}

.underline {
    text-decoration: underline;
}

.overline {
    text-decoration: overline;
}

.line-through {
    text-decoration: line-through;
}

.aqua {
    color: #00bfbf;
}

.aqua-background {
    background-color: #00fafa;
}

.black {
    color: black;
}

.black-background {
    background-color: black;
}

.blue {
    color: #0000bf;
}

.blue-background {
    background-color: #0000fa;
}

.fuchsia {
    color: #bf00bf;
}

.fuchsia-background {
    background-color: #fa00fa;
}

.gray {
    color: #606060;
}

.gray-background {
    background-color: #7d7d7d;
}

.green {
    color: #006000;
}

.green-background {
    background-color: #007d00;
}

.lime {
    color: #00bf00;
}

.lime-background {
    background-color: #00fa00;
}

.maroon {
    color: #600000;
}

.maroon-background {
    background-color: #7d0000;
}

.navy {
    color: #000060;
}

.navy-background {
    background-color: #00007d;
}

.olive {
    color: #606000;
}

.olive-background {
    background-color: #7d7d00;
}

.purple {
    color: #600060;
}

.purple-background {
    background-color: #7d007d;
}

.red {
    color: #bf0000;
}

.red-background {
    background-color: #fa0000;
}

.silver {
    color: #909090;
}

.silver-background {
    background-color: #bcbcbc;
}

.teal {
    color: #006060;
}

.teal-background {
    background-color: #007d7d;
}

.white {
    color: #bfbfbf;
}

.white-background {
    background-color: #fafafa;
}

.yellow {
    color: #bfbf00;
}

.yellow-background {
    background-color: #fafa00;
}

span.icon > [class^="icon-"], span.icon > [class*=" icon-"] {
    cursor: default;
}


.admonitionblock td.icon [class^="icon-"]:before {
    font-size: 2.5em;
    text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
    cursor: default;
}

.admonitionblock td.icon .icon-note:before {
    content: "\f05a";
    color: var(--accent-primary-color);
}

.admonitionblock td.icon .icon-tip:before {
    content: "\f0eb";
    /*text-shadow: 1px 1px 2px var(--accent-primary-color);*/
    color: var(--accent-primary-color);
}

.fa {
    font-family: "FontAwesome", "Helvetica Neue", sans-serif;
}

.admonitionblock td.icon .icon-warning:before {
    content: "\f071";
    color: #bf6900;
}

.admonitionblock td.icon .icon-caution:before {
    content: "\f06d";
    color: #bf3400;
}

.admonitionblock td.icon .icon-important:before {
    content: "\f06a";
    color: #bf0000;
}

.conum {
    display: inline-block;
    color: white !important;
    background-color: black;
    -webkit-border-radius: 100px;
    border-radius: 100px;
    text-align: center;
    width: 20px;
    height: 20px;
    font-size: 12px;
    font-weight: bold;
    line-height: 20px;
    font-family: Arial, sans-serif;
    font-style: normal;
    position: relative;
    top: -2px;
    letter-spacing: -1px;
}

.conum * {
    color: white !important;
}

.conum + b {
    display: none;
}

.conum:after {
    content: attr(data-value);
}

.conum:not([data-value]):empty {
    display: none;
}

h4 {
    color: var(--gray-color);
}

.literalblock > .content > pre, .listingblock > .content > pre {
    -webkit-border-radius: var(--border-radius-default);
    border-radius: var(--border-radius-default);
    margin-left: 0.5em;
    margin-right: 0.5em;
}

.admonitionblock {
    margin-left: .5em;
    margin-right: .5em;
}

.admonitionblock > table {
    border: 1px solid var(--accent-primary-color);
    border-left-width: 4px;
    background-color: var(--admotion-background-color);
    border-collapse: separate;
    -webkit-border-radius: var(--border-radius-default);
    border-radius: var(--border-radius-default);
}

.admonitionblock > table td.icon {
    padding-top: .5em;
    padding-bottom: .5em;
}

.admonitionblock > table td.content {
    padding: .5em .2em;
    color: black;
    font-size: .9em;
    border-left: none;
}

.sidebarblock {
    background-color: var(--sidebar-background-color);
    border-color: #ccc;
}

.sidebarblock > .content > .title {
    color: var(--link-color-default);
}

table.tableblock.grid-all {
    border-collapse: collapse;
    -webkit-border-radius: 0;
    border-radius: 0;
}

table.tableblock.grid-all th.tableblock, table.tableblock.grid-all td.tableblock {
    border-bottom: 1px solid #aaa;
}

#footer {
    background-color: var(--accent-primary-color);
    padding: 2em;
}

#footer-text {
    color: #eee;
    font-size: 0.8em;
    text-align: center;
}
</style>
<style>
pre.rouge table td { padding: 5px; }
pre.rouge table pre { margin: 0; }
pre.rouge, pre.rouge .w {
  color: #24292f;
  background-color: #f6f8fa;
}
pre.rouge .k, pre.rouge .kd, pre.rouge .kn, pre.rouge .kp, pre.rouge .kr, pre.rouge .kt, pre.rouge .kv {
  color: #cf222e;
}
pre.rouge .gr {
  color: #f6f8fa;
}
pre.rouge .gd {
  color: #82071e;
  background-color: #ffebe9;
}
pre.rouge .nb {
  color: #953800;
}
pre.rouge .nc {
  color: #953800;
}
pre.rouge .no {
  color: #953800;
}
pre.rouge .nn {
  color: #953800;
}
pre.rouge .sr {
  color: #116329;
}
pre.rouge .na {
  color: #116329;
}
pre.rouge .nt {
  color: #116329;
}
pre.rouge .gi {
  color: #116329;
  background-color: #dafbe1;
}
pre.rouge .ges {
  font-weight: bold;
  font-style: italic;
}
pre.rouge .kc {
  color: #0550ae;
}
pre.rouge .l, pre.rouge .ld, pre.rouge .m, pre.rouge .mb, pre.rouge .mf, pre.rouge .mh, pre.rouge .mi, pre.rouge .il, pre.rouge .mo, pre.rouge .mx {
  color: #0550ae;
}
pre.rouge .sb {
  color: #0550ae;
}
pre.rouge .bp {
  color: #0550ae;
}
pre.rouge .ne {
  color: #0550ae;
}
pre.rouge .nl {
  color: #0550ae;
}
pre.rouge .py {
  color: #0550ae;
}
pre.rouge .nv, pre.rouge .vc, pre.rouge .vg, pre.rouge .vi, pre.rouge .vm {
  color: #0550ae;
}
pre.rouge .o, pre.rouge .ow {
  color: #0550ae;
}
pre.rouge .gh {
  color: #0550ae;
  font-weight: bold;
}
pre.rouge .gu {
  color: #0550ae;
  font-weight: bold;
}
pre.rouge .s, pre.rouge .sa, pre.rouge .sc, pre.rouge .dl, pre.rouge .sd, pre.rouge .s2, pre.rouge .se, pre.rouge .sh, pre.rouge .sx, pre.rouge .s1, pre.rouge .ss {
  color: #0a3069;
}
pre.rouge .nd {
  color: #8250df;
}
pre.rouge .nf, pre.rouge .fm {
  color: #8250df;
}
pre.rouge .err {
  color: #f6f8fa;
  background-color: #82071e;
}
pre.rouge .c, pre.rouge .ch, pre.rouge .cd, pre.rouge .cm, pre.rouge .cp, pre.rouge .cpf, pre.rouge .c1, pre.rouge .cs {
  color: #6e7781;
}
pre.rouge .gl {
  color: #6e7781;
}
pre.rouge .gt {
  color: #6e7781;
}
pre.rouge .ni {
  color: #24292f;
}
pre.rouge .si {
  color: #24292f;
}
pre.rouge .ge {
  color: #24292f;
  font-style: italic;
}
pre.rouge .gs {
  color: #24292f;
  font-weight: bold;
}
</style>
</head>
<body class="article toc2 toc-left">
<div id="header">
<h1>Basic MVVM Sample</h1>
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#_before_we_start">Before we start</a>
<ul class="sectlevel2">
<li><a href="#_prepare_your_ide">Prepare your IDE</a></li>
<li><a href="#_the_mvvm_pattern_in_short">The MVVM Pattern in short</a></li>
<li><a href="#_properties_fields_classes_and_interfaces">Properties, Fields, Classes, and Interfaces</a></li>
<li><a href="#_inotifypropertychanged">INotifyPropertyChanged</a></li>
<li><a href="#_bindings">Bindings</a></li>
<li><a href="#_create_a_new_project">Create a new Project</a></li>
</ul>
</li>
<li><a href="#_approach_1_implement_inotifypropertychanged_on_our_own">Approach 1 : Implement INotifyPropertyChanged on our own</a>
<ul class="sectlevel2">
<li><a href="#_step_1_create_simpleviewmodel">Step 1: Create SimpleViewModel</a></li>
<li><a href="#_step_2_add_properties_to_our_viewmodel">Step 2: Add Properties to our ViewModel</a></li>
<li><a href="#_step_3_add_simpleviewmodel_to_mainwindowviewmodel">Step 3: Add SimpleViewModel to MainWindowViewModel</a></li>
<li><a href="#_step_4_setup_the_view">Step 4: Setup the View</a></li>
<li><a href="#_step_5_see_it_in_action">Step 5: See it in Action</a></li>
</ul>
</li>
<li><a href="#_approach_2_using_reactiveui">Approach 2 : Using ReactiveUI</a>
<ul class="sectlevel2">
<li><a href="#_step_1_create_reactiveviewmodel">Step 1: Create ReactiveViewModel</a></li>
<li><a href="#_step_2_add_properties_to_our_viewmodel_2">Step 2: Add Properties to our ViewModel</a></li>
<li><a href="#_step_3_add_reactiveviewmodel_to_mainwindowviewmodel">Step 3: Add ReactiveViewModel to MainWindowViewModel</a></li>
<li><a href="#_step_4_modify_the_view">Step 4: Modify the View</a></li>
<li><a href="#_step_5_see_it_in_action_2">Step 5: See it in action</a></li>
</ul>
</li>
<li><a href="#_approach_3_use_any_other_mvvm_framework">Approach 3 : Use any other MVVM Framework</a></li>
<li><a href="#_related">Related</a></li>
</ul>
</div>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>This sample will show you how you can use the <code>MVVM</code>-pattern to receive and process text input by the user. Our sample App will have one <code>TextBox</code> where the users can enter their name and another <code>TextBox</code> where we will present some personal greetings.</p>
</div>
<!-- toc disabled -->
<h3 id="_difficulty" class="discrete">Difficulty</h3>
<div class="paragraph">
<p>🐣 Beginner 🐣</p>
</div>
<h3 id="_buzz_words" class="discrete">Buzz-Words</h3>
<div class="paragraph">
<p>MVVM, Model-View-ViewModel, ReactiveUI, INotifyPropertyChanged, XAML, Binding</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_before_we_start">Before we start</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_prepare_your_ide">Prepare your IDE</h3>
<div class="paragraph">
<p>Please make sure you have setup your IDE according to the [<a href="https://docs.avaloniaui.net/docs/get-started/set-up-an-editor" target="_blank" rel="noopener">docs</a>]. Moreover you should have read the [<a href="https://docs.avaloniaui.net/docs/welcome" target="_blank" rel="noopener">Getting Started guide</a>].</p>
</div>
</div>
<div class="sect2">
<h3 id="_the_mvvm_pattern_in_short">The MVVM Pattern in short</h3>
<div class="paragraph">
<p>We will use the <code>MVVM</code>-pattern in this sample. <code>MVVM</code> is short for <strong>M</strong>odel - <strong>V</strong>iew - <strong>V</strong>iew<strong>M</strong>odel and describes a software architectural pattern that will split the code base into three pieces:.</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="dlist">
<dl>
<dt class="hdlist1">Model</dt>
<dd>
<p>The <code>Model</code> will store and handle your data. The model doesn&#8217;t know anything about your <code>View</code> or <code>ViewModel</code>.</p>
</dd>
<dt class="hdlist1">View</dt>
<dd>
<p>The <code>View</code> can be thought as the <strong>U</strong>ser <strong>I</strong>nterface and will interact with your <code>ViewModel</code>.</p>
</dd>
<dt class="hdlist1">ViewModel</dt>
<dd>
<p>The <code>ViewModel</code> can be seen as a mediator between the <code>View</code> and the <code>Model</code>. It will know about the <code>Model</code> and is known by the <code>View</code>, but it doesn&#8217;t know anything about the <code>View</code>. It can transform and convert the data provided by the <code>Model</code>, if needed.</p>
</dd>
</dl>
</div>
</div>
</div>
<div class="paragraph">
<p>The <code>MVVM</code> pattern helps us to focus on either of the three parts. If we get new values in our <code>Model</code>, the <code>ViewModel</code> will notify the <code>View</code> about it and the <code>View</code> can update itself. We don&#8217;t have to do it manually. If you want to read more about it, here is a collection of useful links:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://docs.avaloniaui.net/docs/concepts/the-mvvm-pattern/" target="_blank" rel="noopener">[Avalonia Docs]</a></p>
</li>
<li>
<p><a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel" target="_blank" rel="noopener">[Wikipedia]</a></p>
</li>
<li>
<p><a href="https://www.codeproject.com/Articles/278901/MVVM-Pattern-Made-Simple" target="_blank" rel="noopener">[CodeProject]</a></p>
</li>
<li>
<p><a href="https://www.tutorialspoint.com/mvvm/index.htm" target="_blank" rel="noopener">[tutorialspoint]</a></p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_properties_fields_classes_and_interfaces">Properties, Fields, Classes, and Interfaces</h3>
<div class="paragraph">
<p>C# uses <code>Interfaces</code> to define contracts that <code>Classes</code> can satisfy by providing an implementation for the <code>Members</code> defined in the specific <code>Interface</code>. <code>Members</code> of a <code>Class</code> are comprised of <code>Fields</code> and <code>Methods</code>. <code>Methods</code> are code blocks that can be executed. <code>Fields</code> are simply variables that can hold data. <code>Properties</code> are used to read, write, or compute the value of private <code>Fields</code>.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>More on Interfaces: <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/interface" target="_blank" rel="noopener">[Microsoft Docs]</a></p>
</li>
<li>
<p>More on Classes: <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/class" target="_blank" rel="noopener">[Microsoft Docs]</a></p>
</li>
<li>
<p>More on Members: <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/members" target="_blank" rel="noopener">[Microsoft Docs]</a></p>
</li>
<li>
<p>More on Methods: <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/methods" target="_blank" rel="noopener">[Microsoft Docs]</a></p>
</li>
<li>
<p>More on Fields: <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/fields" target="_blank" rel="noopener">[Microsoft Docs]</a></p>
</li>
<li>
<p>More on Properties: <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties" target="_blank" rel="noopener">[Microsoft Docs]</a></p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_inotifypropertychanged">INotifyPropertyChanged</h3>
<div class="paragraph">
<p>Our <code>View</code> needs a way to know when a <code>Property</code> has changed and that the <code>View</code> should update itself. To achieve this there is an <code>Interface</code> called <code>INotifyPropertyChanged</code> which implements the event <code>PropertyChanged</code>. If our <code>ViewModel</code> implements this interface, we can send update notifications to our <code>View</code> or any other class listening to that event.</p>
</div>
<div class="paragraph">
<p>More: <a href="https://docs.microsoft.com/en-US/dotnet/api/system.componentmodel.inotifypropertychanged?view=net-6.0" target="_blank" rel="noopener">[Microsoft Docs]</a></p>
</div>
</div>
<div class="sect2">
<h3 id="_bindings">Bindings</h3>
<div class="paragraph">
<p>Avalonia uses <code>Controls</code> to represent functional components of a GUI Layout.  Example <code>Controls</code> include <a href="https://docs.avaloniaui.net/docs/reference/controls/scrollbar">[<code>ScrollBar</code>]</a>, [<a href="https://docs.avaloniaui.net/docs/reference/controls/buttons/button" target="_blank" rel="noopener">[<code>Button</code></a>], and [<a href="https://docs.avaloniaui.net/docs/reference/controls/detailed-reference/textbox" target="_blank" rel="noopener"><code>TextBox</code></a>]. <code>Properties</code> (like the <code>Text</code>-property of a <code>TextBox</code>) describe and allow interaction with the <code>Controls</code>.</p>
</div>
<div class="paragraph">
<p>We need a way to connect the <code>Properties</code> of our controls with the <code>Properties</code> in our <code>ViewModel</code>. Luckily we have  [<a href="https://docs.avaloniaui.net/docs/basics/data/data-binding/" target="_blank" rel="noopener"><code>Bindings</code></a>] which will do this for us. The <code>Binding</code> will update the <code>View</code> whenever the <code>ViewModel</code> reports changes and will also update the <code>ViewModel</code> whenever the user interacts with the <code>View</code>.</p>
</div>
</div>
<div class="sect2">
<h3 id="_create_a_new_project">Create a new Project</h3>
<div class="paragraph">
<p>Create a new Avalonia MVVM Application using one of the following procedures, depending on your preferred development environment.</p>
</div>
<div class="sect3">
<h4 id="_microsoft_visual_studio">Microsoft Visual Studio</h4>
<div class="paragraph">
<p>Create a new "Avalonia MVVM Application" Project via <code>File ► New Project ► Avalonia MVVM Project</code>:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_docs/NewProjectVS.png" alt="New Project dialog in Microsoft Visual Studio">
</div>
</div>
</div>
<div class="sect3">
<h4 id="_jetbrains_rider">JetBrains Rider</h4>
<div class="paragraph">
<p>Create a new "Avalonia .NET Core MVVM App" Project via <code>File ► New&#8230;&#8203; ► Avalonia .NET Core MVVM App</code>:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_docs/NewProjectRider.png" alt="New Project dialog in JetBrains Rider">
</div>
</div>
</div>
<div class="sect3">
<h4 id="_command_line">Command Line</h4>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="txt">dotnet new avalonia.mvvm -o BasicMvvmSample
cd BasicMvvmSample</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_approach_1_implement_inotifypropertychanged_on_our_own">Approach 1 : Implement INotifyPropertyChanged on our own</h2>
<div class="sectionbody">
<div class="paragraph">
<p>In this sample we will show you how you can implement and use the interface <code>INotifyPropertyChanged</code> on your own.</p>
</div>
<div class="sect2">
<h3 id="_step_1_create_simpleviewmodel">Step 1: Create SimpleViewModel</h3>
<div class="paragraph">
<p>Add a new class called "SimpleViewModel" to the <code>ViewModels</code> project folder. This class should implement <code>INotifyPropertyChanged</code> as shown below:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="c1">// Remember to add this to your usings</span>
<span class="k">using</span> <span class="nn">System.ComponentModel</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">BasicMvvmSample.ViewModels</span>
<span class="p">{</span>
    <span class="c1">// This is our simple ViewModel. We need to implement the interface "INotifyPropertyChanged"</span>
    <span class="c1">// in order to notify the View if any of our properties changed.</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">SimpleViewModel</span> <span class="p">:</span> <span class="n">INotifyPropertyChanged</span>
    <span class="p">{</span>
        <span class="c1">// This event is implemented by "INotifyPropertyChanged" and is all we need to inform</span>
        <span class="c1">// our view about changes.</span>
        <span class="k">public</span> <span class="k">event</span> <span class="n">PropertyChangedEventHandler</span><span class="p">?</span> <span class="n">PropertyChanged</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>For convenience we will now add a method to our class, which will raise the event for us. We need to provide the name of the property which has changed. If we add <code>[CallerMemberName]</code> to the method argument, the compiler will add the property name for us. Add the <code>using</code> statement to the top of <code>SimpleViewModel.cs</code> and the method inside the <code>SimpleViewModel</code> class.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="c1">// Remember to add this to your usings</span>
<span class="k">using</span> <span class="nn">System.Runtime.CompilerServices</span><span class="p">;</span>

<span class="p">[...]</span>

<span class="k">private</span> <span class="k">void</span> <span class="nf">RaisePropertyChanged</span><span class="p">([</span><span class="n">CallerMemberName</span><span class="p">]</span> <span class="kt">string</span><span class="p">?</span> <span class="n">propertyName</span> <span class="p">=</span> <span class="k">null</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">PropertyChanged</span><span class="p">?.</span><span class="nf">Invoke</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="k">new</span> <span class="nf">PropertyChangedEventArgs</span><span class="p">(</span><span class="n">propertyName</span><span class="p">));</span>
<span class="p">}</span></code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_2_add_properties_to_our_viewmodel">Step 2: Add Properties to our ViewModel</h3>
<div class="paragraph">
<p>We want the user to be able to enter their name. This text will later be used to greet the user. So let&#8217;s add the properties <code>Name</code> and <code>Greeting</code> to our <code>ViewModel</code>.  Add the following code inside the <code>SimpleViewModel</code> class.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="k">private</span> <span class="kt">string</span><span class="p">?</span> <span class="n">_Name</span><span class="p">;</span> <span class="c1">// This is our backing field for Name</span>

<span class="k">public</span> <span class="kt">string</span><span class="p">?</span> <span class="n">Name</span>
<span class="p">{</span>
    <span class="k">get</span>
    <span class="p">{</span>
        <span class="k">return</span> <span class="n">_Name</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">set</span>
    <span class="p">{</span>
        <span class="c1">// We only want to update the UI if the Name actually changed, so we check if the value is actually new</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">_Name</span> <span class="p">!=</span> <span class="k">value</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="c1">// 1. update our backing field</span>
            <span class="n">_Name</span> <span class="p">=</span> <span class="k">value</span><span class="p">;</span>

            <span class="c1">// 2. We call RaisePropertyChanged() to notify the UI about changes.</span>
            <span class="c1">// We can omit the property name here because [CallerMemberName] will provide it for us.</span>
            <span class="nf">RaisePropertyChanged</span><span class="p">();</span>

            <span class="c1">// 3. Greeting also changed. So let's notify the UI about it.</span>
            <span class="nf">RaisePropertyChanged</span><span class="p">(</span><span class="k">nameof</span><span class="p">(</span><span class="n">Greeting</span><span class="p">));</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// Greeting will change based on a Name.</span>
<span class="k">public</span> <span class="kt">string</span> <span class="n">Greeting</span>
<span class="p">{</span>
    <span class="k">get</span>
    <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrEmpty</span><span class="p">(</span><span class="n">Name</span><span class="p">))</span>
        <span class="p">{</span>
            <span class="c1">// If no Name is provided, use a default Greeting</span>
            <span class="k">return</span> <span class="s">"Hello World from Avalonia.Samples"</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">else</span>
        <span class="p">{</span>
            <span class="c1">// else greet the User.</span>
            <span class="k">return</span> <span class="s">$"Hello </span><span class="p">{</span><span class="n">Name</span><span class="p">}</span><span class="s">"</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_3_add_simpleviewmodel_to_mainwindowviewmodel">Step 3: Add SimpleViewModel to MainWindowViewModel</h3>
<div class="paragraph">
<p>Remember that the <code>View</code> implements the User Interface. Our view will only consist of one [<a href="https://docs.avaloniaui.net/docs/concepts/toplevel" target="_blank" rel="noopener"><code>Window</code></a>] called <code>MainWindow</code>. Its [<a href="https://docs.avaloniaui.net/docs/basics/data/data-binding/data-context" target="_blank" rel="noopener"><code>DataContext</code></a>], which describes the default location where controls should look values when binding, is the class <code>MainWindowViewModel</code> which was already added by the template when we first created our project. We will just add an instance of our <code>SimpleViewModel</code> to it. Add the following code to the <code>MainWindowViewModel</code> class in <code>MainWindowViewModel.cs</code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="c1">// Add our SimpleViewModel.</span>
<span class="c1">// Note: We need at least a get-accessor for our Properties.</span>
<span class="k">public</span> <span class="n">SimpleViewModel</span> <span class="n">SimpleViewModel</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">SimpleViewModel</span><span class="p">();</span></code></pre>
</div>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">NOTE</dt>
<dd>
<p>Depending on the template and setting used to create your project you may find a class called <code>MainViewModel</code> instead of <code>MainWindowViewModel</code>. In this case please make sure the correct name.</p>
</dd>
</dl>
</div>
</div>
<div class="sect2">
<h3 id="_step_4_setup_the_view">Step 4: Setup the View</h3>
<div class="paragraph">
<p>Now we can start with the UI layout. Our View will be written in [<a href="https://docs.avaloniaui.net/docs/basics/user-interface/introduction-to-xaml" target="_blank" rel="noopener"><code>XAML</code></a>] which is an XML-based markup language that is used by many UI frameworks. The code modifications shown below will be applied to <code>MainWindow.axaml</code>.</p>
</div>
<div class="sect3">
<h4 id="_setup_the_window">Setup the Window</h4>
<div class="paragraph">
<p>Replace the content of <code>MainWindow.axaml</code> with the following code.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="xml"><span class="nt">&lt;Window</span> <span class="na">xmlns=</span><span class="s">"https://github.com/avaloniaui"</span>
        <span class="na">xmlns:x=</span><span class="s">"http://schemas.microsoft.com/winfx/2006/xaml"</span>
        <span class="na">xmlns:vm=</span><span class="s">"using:BasicMvvmSample.ViewModels"</span>
        <span class="na">xmlns:d=</span><span class="s">"http://schemas.microsoft.com/expression/blend/2008"</span>
        <span class="na">xmlns:mc=</span><span class="s">"http://schemas.openxmlformats.org/markup-compatibility/2006"</span>
        <span class="na">mc:Ignorable=</span><span class="s">"d"</span> <span class="na">d:DesignWidth=</span><span class="s">"800"</span> <span class="na">d:DesignHeight=</span><span class="s">"450"</span>
        <span class="na">x:CompileBindings=</span><span class="s">"True"</span>
        <span class="na">x:DataType=</span><span class="s">"vm:MainWindowViewModel"</span>
        <span class="na">x:Class=</span><span class="s">"BasicMvvmSample.Views.MainWindow"</span>
        <span class="na">Icon=</span><span class="s">"/Assets/avalonia-logo.ico"</span>
        <span class="na">Title=</span><span class="s">"BasicMvvmSample"</span><span class="nt">&gt;</span>
<span class="nt">&lt;/Window&gt;</span></code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Note that the lines <code>x:CompileBindings="True"</code> and <code>x:DataType="vm:MainWindowViewModel"</code> enables [<a href="https://docs.avaloniaui.net/docs/data-binding/compiledbindings" target="_blank" rel="noopener"><code>CompiledBindings</code></a>] in our <code>Window</code>. Enabling <code>CompiledBindings</code> is completely optional but has many benefits such as better performance and a better debugging experience.
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_add_the_content_to_mainwindow">Add the Content to MainWindow</h4>
<div class="paragraph">
<p>Our <code>View</code> will be built using two [<a href="https://docs.avaloniaui.net/docs/controls/textbox" target="_blank" rel="noopener"><code>TextBoxes</code></a>]: one for the user to enter their name and another one to present the greeting. As a <code>Window</code> can only have one child, we need to wrap our controls in a [<a href="https://docs.avaloniaui.net/docs/layout" target="_blank" rel="noopener"><code>Panel</code></a>]. We will choose a [<a href="https://docs.avaloniaui.net/docs/controls/stackpanel" target="_blank" rel="noopener"><code>StackPanel</code></a>], but you can use any other <code>Panel</code> variant to give you greater control over the layout.</p>
</div>
<div class="paragraph">
<p>The <code>StackPanel&#8217;s `DataContext</code> will [<a href="https://docs.avaloniaui.net/docs/data-binding/bindings" target="_blank" rel="noopener">bind</a>] to our <code>SimpleViewModel</code>. As the <code>DataContext</code> is inherited from the parent control, our <code>TextBoxes</code> will have the same <code>DataContext</code>. Therefore we can just bind <code>TextBox.Text</code> to <code>Name</code> and <code>Greeting</code> respectively.  The complete <code>MainWindow.axaml</code> code is shown below:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="xml"><span class="nt">&lt;Window</span> <span class="na">xmlns=</span><span class="s">"https://github.com/avaloniaui"</span>
        <span class="na">xmlns:x=</span><span class="s">"http://schemas.microsoft.com/winfx/2006/xaml"</span>
        <span class="na">xmlns:vm=</span><span class="s">"using:BasicMvvmSample.ViewModels"</span>
        <span class="na">xmlns:d=</span><span class="s">"http://schemas.microsoft.com/expression/blend/2008"</span>
        <span class="na">xmlns:mc=</span><span class="s">"http://schemas.openxmlformats.org/markup-compatibility/2006"</span>
        <span class="na">mc:Ignorable=</span><span class="s">"d"</span> <span class="na">d:DesignWidth=</span><span class="s">"800"</span> <span class="na">d:DesignHeight=</span><span class="s">"450"</span>
        <span class="na">x:CompileBindings=</span><span class="s">"True"</span>
        <span class="na">x:DataType=</span><span class="s">"vm:MainWindowViewModel"</span>
        <span class="na">x:Class=</span><span class="s">"BasicMvvmSample.Views.MainWindow"</span>
        <span class="na">Icon=</span><span class="s">"/Assets/avalonia-logo.ico"</span>
        <span class="na">Title=</span><span class="s">"BasicMvvmSample"</span><span class="nt">&gt;</span>

	<span class="c">&lt;!-- This is just used by the designer / previewer --&gt;</span>
	<span class="nt">&lt;Design.DataContext&gt;</span>
        	<span class="nt">&lt;vm:MainWindowViewModel/&gt;</span>
    	<span class="nt">&lt;/Design.DataContext&gt;</span>

	<span class="c">&lt;!-- Our Simple ViewModel--&gt;</span>
	<span class="nt">&lt;StackPanel</span> <span class="na">DataContext=</span><span class="s">"{Binding SimpleViewModel}"</span> <span class="na">Spacing=</span><span class="s">"10"</span><span class="nt">&gt;</span>
		<span class="nt">&lt;TextBlock&gt;</span>Enter your Name:<span class="nt">&lt;/TextBlock&gt;</span>
		<span class="nt">&lt;TextBox</span> <span class="na">Text=</span><span class="s">"{Binding Name}"</span> <span class="nt">/&gt;</span>
		<span class="nt">&lt;TextBox</span> <span class="na">Text=</span><span class="s">"{Binding Greeting, Mode=OneWay}"</span>
				 <span class="na">IsReadOnly=</span><span class="s">"True"</span>
				 <span class="na">FontWeight=</span><span class="s">"Bold"</span> <span class="nt">/&gt;</span>
	<span class="nt">&lt;/StackPanel&gt;</span>
<span class="nt">&lt;/Window&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Please note that we use <code>Mode=OneWay</code> in the <code>Binding</code> for <code>Greeting</code>, because <code>Greeting</code> is read-only and the default <code>Mode</code> of <code>TextBox.Text</code> is <code>TwoWay</code>. Moreover we made this <code>TextBox</code> read-only, so the user will not be able to change the <code>Text</code> by accident.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_5_see_it_in_action">Step 5: See it in Action</h3>
<div class="paragraph">
<p>In your IDE hit <code>Run</code> or <code>Debug</code>. You should see the following result:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_docs/result.png" alt="Result">
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_approach_2_using_reactiveui">Approach 2 : Using ReactiveUI</h2>
<div class="sectionbody">
<div class="paragraph">
<p>We don&#8217;t need to implement all the boilerplate code on our own over and over again. Instead, we can use any existing <code>MVVM</code> framework out there. [<a href="https://www.reactiveui.net" target="_blank" rel="noopener">ReactiveUI</a>] is a popular MVVM framework designed for <code>.NET</code>.  If you create a new Avalonia MVVM Project, you will have <code>ReactiveUI</code> (or <code>CommunityToolkit.Mvvm</code> based on your choice) installed by default. So let&#8217;s see how we can use <code>ReactiveUI</code> to achieve the same results as <strong>Approach 1</strong> above.</p>
</div>
<div class="sect2">
<h3 id="_step_1_create_reactiveviewmodel">Step 1: Create ReactiveViewModel</h3>
<div class="paragraph">
<p>As before, we start by adding a new class called "ReactiveViewModel" into the <code>ViewModels</code> folder. This time we will use <code>ReactiveObject</code> as our base class. This base class already implements <code>INotifyPropertyChanged</code>, so we don&#8217;t need to implement it again.  Make sure your <code>ReactiveViewModel.cs</code> file looks like the code below:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="k">using</span> <span class="nn">ReactiveUI</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">BasicMvvmSample.ViewModels</span>
<span class="p">{</span>
    <span class="c1">// Instead of implementing "INotifyPropertyChanged" on our own we use "ReactiveObject" as</span>
    <span class="c1">// our base class. Read more about it here: https://www.reactiveui.net</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">ReactiveViewModel</span> <span class="p">:</span> <span class="n">ReactiveObject</span>
    <span class="p">{</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_2_add_properties_to_our_viewmodel_2">Step 2: Add Properties to our ViewModel</h3>
<div class="paragraph">
<p>Our <code>Name</code>-property now has less boilerplate code in the setter, as we can use ReactiveUI&#8217;s <code>RaiseAndSetIfChanged</code> method. Add the code below to the <code>ReactiveViewModel</code> class in <code>ReactiveViewModel.cs</code></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="k">private</span> <span class="kt">string</span><span class="p">?</span> <span class="n">_Name</span><span class="p">;</span> <span class="c1">// This is our backing field for Name</span>

<span class="k">public</span> <span class="kt">string</span><span class="p">?</span> <span class="n">Name</span>
<span class="p">{</span>
    <span class="k">get</span>
    <span class="p">{</span>
        <span class="k">return</span> <span class="n">_Name</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">set</span>
    <span class="p">{</span>
        <span class="c1">// We can use "RaiseAndSetIfChanged" to check if the value changed and automatically notify the UI</span>
        <span class="k">this</span><span class="p">.</span><span class="nf">RaiseAndSetIfChanged</span><span class="p">(</span><span class="k">ref</span> <span class="n">_Name</span><span class="p">,</span> <span class="k">value</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p><code>Greeting</code> does not need to be modified and can be copied from <code>SimpleViewModel</code>.  So either copy the code below or copy the same code from <code>SimpleViewModel</code> and place it in the <code>ReactiveViewModel</code> class in <code>ReactiveViewModel.cs</code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="c1">// Greeting will change based on a Name.</span>
<span class="k">public</span> <span class="kt">string</span> <span class="n">Greeting</span>
<span class="p">{</span>
    <span class="k">get</span>
    <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrEmpty</span><span class="p">(</span><span class="n">Name</span><span class="p">))</span>
        <span class="p">{</span>
            <span class="c1">// If no Name is provided, use a default Greeting</span>
            <span class="k">return</span> <span class="s">"Hello World from Avalonia.Samples"</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">else</span>
        <span class="p">{</span>
            <span class="c1">// else greet the User.</span>
            <span class="k">return</span> <span class="s">$"Hello </span><span class="p">{</span><span class="n">Name</span><span class="p">}</span><span class="s">"</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>But wait, how do we notify the <code>View</code> that <code>Greeting</code> should update? In ReactiveUI we can use [<a href="https://www.reactiveui.net/docs/handbook/when-any/#whenanyvalue" target="_blank" rel="noopener"><code>WhenAnyValue</code></a>] to listen and react to property changes. We will setup that listener in the constructor of our <code>ReactiveViewModel</code>.  Add the constructor code shown below to the <code>ReactiveViewModel</code> class.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="k">public</span> <span class="nf">ReactiveViewModel</span><span class="p">()</span>
<span class="p">{</span>
    <span class="c1">// We can listen to any property changes with "WhenAnyValue" and do whatever we want in "Subscribe".</span>
    <span class="k">this</span><span class="p">.</span><span class="nf">WhenAnyValue</span><span class="p">(</span><span class="n">o</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">Name</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">Subscribe</span><span class="p">(</span><span class="n">o</span> <span class="p">=&gt;</span> <span class="k">this</span><span class="p">.</span><span class="nf">RaisePropertyChanged</span><span class="p">(</span><span class="k">nameof</span><span class="p">(</span><span class="n">Greeting</span><span class="p">)));</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>To explain the above code in short: <code>WhenAnyValue</code> will listen to changes of the property specified in the [<a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/lambda-expressions" target="_blank" rel="noopener">lambda</a>]. In <code>Subscribe</code> we define what should happen if the value has changed. In our case, we want to <code>RaisePropertyChanged</code> for <code>Greeting</code>.</p>
</div>
</div>
<div class="sect2">
<h3 id="_step_3_add_reactiveviewmodel_to_mainwindowviewmodel">Step 3: Add ReactiveViewModel to MainWindowViewModel</h3>
<div class="paragraph">
<p>Similarly to how we added <code>SimpleViewModel</code> we can add <code>ReactiveViewModel</code> to <code>MainWindowViewModel</code>. Add the code below to the <code>MainWindowViewModel</code> class in <code>MainWindowViewModel.cs</code></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="c1">// Add our ReactiveViewModel</span>
<span class="k">public</span> <span class="n">ReactiveViewModel</span> <span class="n">ReactiveViewModel</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">ReactiveViewModel</span><span class="p">();</span></code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_4_modify_the_view">Step 4: Modify the View</h3>
<div class="paragraph">
<p>We can use both <code>ViewModels</code> side by side. To demonstrate this, we will wrap both <code>Views</code> in a <code>TabControl</code>. As our properties have the same names, we can just copy &amp; paste the view we have and modify the <code>DataContext</code>.  Modify <code>MainWindow.axaml</code> to add the <code>ReactiveUI</code> view and to wrap both the <code>ReactiveUI</code> and <code>SimpleViewModel</code> controls in a <code>TabControl</code> as shown below:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="xml"><span class="nt">&lt;Window</span> <span class="na">xmlns=</span><span class="s">"https://github.com/avaloniaui"</span>
        <span class="na">xmlns:x=</span><span class="s">"http://schemas.microsoft.com/winfx/2006/xaml"</span>
        <span class="na">xmlns:vm=</span><span class="s">"using:BasicMvvmSample.ViewModels"</span>
        <span class="na">xmlns:d=</span><span class="s">"http://schemas.microsoft.com/expression/blend/2008"</span>
        <span class="na">xmlns:mc=</span><span class="s">"http://schemas.openxmlformats.org/markup-compatibility/2006"</span>
        <span class="na">mc:Ignorable=</span><span class="s">"d"</span> <span class="na">d:DesignWidth=</span><span class="s">"800"</span> <span class="na">d:DesignHeight=</span><span class="s">"450"</span>
        <span class="na">x:CompileBindings=</span><span class="s">"True"</span>
        <span class="na">x:DataType=</span><span class="s">"vm:MainWindowViewModel"</span>
        <span class="na">x:Class=</span><span class="s">"BasicMvvmSample.Views.MainWindow"</span>
        <span class="na">Icon=</span><span class="s">"/Assets/avalonia-logo.ico"</span>
        <span class="na">Title=</span><span class="s">"BasicMvvmSample"</span><span class="nt">&gt;</span>

	<span class="c">&lt;!-- This is just used by the designer / previewer --&gt;</span>
    	<span class="nt">&lt;Design.DataContext&gt;</span>
        	<span class="nt">&lt;vm:MainWindowViewModel/&gt;</span>
    	<span class="nt">&lt;/Design.DataContext&gt;</span>

	<span class="nt">&lt;TabControl&gt;</span>

		<span class="c">&lt;!-- Our Simple ViewModel --&gt;</span>
		<span class="nt">&lt;TabItem</span> <span class="na">Header=</span><span class="s">"Simple ViewModel"</span> <span class="nt">&gt;</span>
			<span class="nt">&lt;StackPanel</span> <span class="na">DataContext=</span><span class="s">"{Binding SimpleViewModel}"</span> <span class="na">Spacing=</span><span class="s">"10"</span><span class="nt">&gt;</span>
				<span class="nt">&lt;TextBlock&gt;</span>Enter your Name:<span class="nt">&lt;/TextBlock&gt;</span>
				<span class="nt">&lt;TextBox</span> <span class="na">Text=</span><span class="s">"{Binding Name}"</span> <span class="nt">/&gt;</span>
				<span class="nt">&lt;TextBox</span> <span class="na">Text=</span><span class="s">"{Binding Greeting, Mode=OneWay}"</span>
						 <span class="na">IsReadOnly=</span><span class="s">"True"</span>
						 <span class="na">FontWeight=</span><span class="s">"Bold"</span> <span class="nt">/&gt;</span>
			<span class="nt">&lt;/StackPanel&gt;</span>
		<span class="nt">&lt;/TabItem&gt;</span>

		<span class="c">&lt;!-- Our ReactiveViewModel --&gt;</span>
		<span class="nt">&lt;TabItem</span> <span class="na">Header=</span><span class="s">"Reactive UI"</span> <span class="nt">&gt;</span>
			<span class="nt">&lt;StackPanel</span> <span class="na">DataContext=</span><span class="s">"{Binding ReactiveViewModel}"</span> <span class="na">Spacing=</span><span class="s">"10"</span><span class="nt">&gt;</span>
				<span class="nt">&lt;TextBlock&gt;</span>Enter your Name:<span class="nt">&lt;/TextBlock&gt;</span>
				<span class="nt">&lt;TextBox</span> <span class="na">Text=</span><span class="s">"{Binding Name}"</span> <span class="nt">/&gt;</span>
				<span class="nt">&lt;TextBox</span> <span class="na">Text=</span><span class="s">"{Binding Greeting, Mode=OneWay}"</span>
						 <span class="na">IsReadOnly=</span><span class="s">"True"</span>
						 <span class="na">FontWeight=</span><span class="s">"Bold"</span> <span class="nt">/&gt;</span>
			<span class="nt">&lt;/StackPanel&gt;</span>
		<span class="nt">&lt;/TabItem&gt;</span>

	<span class="nt">&lt;/TabControl&gt;</span>
<span class="nt">&lt;/Window&gt;</span></code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_5_see_it_in_action_2">Step 5: See it in action</h3>
<div class="paragraph">
<p>In your IDE hit <code>Run</code> or <code>Debug</code>. You should see the following result:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_docs/result_2.png" alt="Result">
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_approach_3_use_any_other_mvvm_framework">Approach 3 : Use any other MVVM Framework</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Even though Avalonia ships ReactiveUI by default, you are not tied to it. You can use it side by side with other MVVM Frameworks or can completely replace it. Among others here is a short list of popular MVVM Frameworks for your consideration:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://www.reactiveui.net" target="_blank" rel="noopener">[ReactiveUI]</a></p>
</li>
<li>
<p><a href="https://docs.microsoft.com/en-us/windows/communitytoolkit/mvvm/introduction" target="_blank" rel="noopener">[CommunityToolkit.MVVM]</a></p>
</li>
<li>
<p><a href="https://docs.prismlibrary.com/docs/" target="_blank" rel="noopener">[Prism]</a></p>
</li>
<li>
<p><a href="https://caliburnmicro.com"" target="_blank" rel="noopener">[Caliburn Micro]</a></p>
</li>
<li>
<p>&#8230;&#8203; And many more.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_related">Related</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Are you looking for more advanced tutorials? Find them here:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://github.com/AvaloniaUI/Avalonia.Samples/tree/main/src/Avalonia.Samples/CompleteApps/SimpleToDoList/" target="_blank" rel="noopener">[ToDo List App]</a></p>
</li>
<li>
<p><a href="https://docs.avaloniaui.net/docs/tutorials/music-store-app/" target="_blank" rel="noopener">[Music Store App]</a></p>
</li>
<li>
<p><a href="https://github.com/AvaloniaCommunity/awesome-avalonia#tutorials" target="_blank" rel="noopener">[Awesome-Avalonia]</a></p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2025-11-06 19:42:36 UTC
</div>
</div>
</body>
</html>